<template>
  <view class="mxp-calendar" :class="cusClass" :style="[cusStyle, { backgroundColor: background, borderRadius: '10px 10px 0px 0px' }]">
    <!-- header -->
    <slot name="header" :todayData="data.todayData">
      <view v-if="!pickerdata.show" class="header-view">
        <view class="header-btn-box" @click.stop="onPre">
          <mxp-icon name="zuojiantou" size="18px"></mxp-icon>
        </view>
        <text class="header-title" @click="openPickerDate">
          {{ (data.todayData!.year || '') + '年' + (data.todayData!.month || '') + '月' }}
        </text>
        <view class="header-btn-box" @click.stop="onNext">
          <mxp-icon name="youjiantou" size="18px"></mxp-icon>
        </view>
        <text class="back-today" :style="{ background: themeColor }" @click="backToday">回到今天</text>
      </view>
    </slot>
    <!-- 日历 -->
    <view v-if="pickerdata.show" style="position: absolute; z-index: 1000; width: 100%; height: 100%">
      <mxp-toolbar title="请选择年月" :confirmColor="themeColor" @cancel="cancelPicker" @confirm="submitPicker"></mxp-toolbar>
      <view style="height: 48px"></view>
      <picker-view
        id="firstList"
        class="picker-view"
        :value="pickerdata.value"
        :style="{ height: `200px` }"
        indicatorStyle="height: 40px;line-height: 40px;"
        @change="changePicker"
      >
        <picker-view-column>
          <view v-for="(item, index) in pickerdata.yearList" :key="index" class="picker-item">
            <text style="font-size: 14px; color: #333333">{{ item }}年</text>
          </view>
        </picker-view-column>
        <picker-view-column>
          <view v-for="(item, index) in pickerdata.monthList" :key="index" class="picker-item">
            <text style="font-size: 14px; color: #333333">{{ item }}月</text>
          </view>
        </picker-view-column>
      </picker-view>
    </view>
    <!-- 日历 -->
    <view style="width: 100%" :style="{ opacity: pickerdata.show ? 0 : 1 }">
      <mxp-line v-if="showLine"></mxp-line>
      <view class="mxp-calendar-vessel">
        <view class="weeks-view">
          <view v-if="startWeek === 'sun'" class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[6] || '周日' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[0] || '周一' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[1] || '周二' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[2] || '周三' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[3] || '周四' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[4] || '周五' }}</text>
          </view>
          <view class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[5] || '周六' }}</text>
          </view>
          <view v-if="startWeek === 'mon'" key="view-2" class="week-day-view">
            <text class="day-text" :style="[weekTextStyle]">{{ weekText[6] || '周日' }}</text>
          </view>
        </view>
        <mxp-line v-if="showLine"></mxp-line>
        <!-- 滑动切换 -->
        <swiper
          v-if="slideSwitchMode !== 'none'"
          :style="[swiperStyle]"
          class="swiper"
          :duration="500"
          :vertical="slideSwitchMode === 'vertical'"
          :circular="true"
          :current="data.swiperCurrent"
          :disableProgrammaticAnimation="true"
          :skipHiddenItemLayout="true"
          @change="swiperChange"
        >
          <!-- 月或周日历 -->

          <swiper-item>
            <mxp-calendar-month
              :monthViewData="data.preMonthViewData"
              :selectedDayData="data.selectedDayData"
              :selected="selected"
              :dayHeight="dayHeight"
              :badgeOptions="badgeOptions"
              :lunar="lunar"
              :themeColor="themeColor"
              :dayColor="dayColor"
              :daySize="daySize"
              :dayBold="dayBold"
              :lunarColor="lunarColor"
              :lunarSize="lunarSize"
              :startText="startText"
              :endText="endText"
              :month="data.preMonth"
              :foldStatus="data.foldStatus"
              :showMonth="showMonth"
              @change="choiceDate"
            ></mxp-calendar-month>
          </swiper-item>
          <swiper-item>
            <mxp-calendar-month
              :monthViewData="data.currentMonthViewData"
              :selectedDayData="data.selectedDayData"
              :selected="selected"
              :dayHeight="dayHeight"
              :badgeOptions="badgeOptions"
              :lunar="lunar"
              :themeColor="themeColor"
              :dayColor="dayColor"
              :daySize="daySize"
              :dayBold="dayBold"
              :lunarColor="lunarColor"
              :lunarSize="lunarSize"
              :startText="startText"
              :endText="endText"
              :month="data.currentMonth"
              :foldStatus="data.foldStatus"
              :showMonth="showMonth"
              @change="choiceDate"
            ></mxp-calendar-month>
          </swiper-item>
          <swiper-item>
            <mxp-calendar-month
              :monthViewData="data.nextMonthViewData"
              :selectedDayData="data.selectedDayData"
              :badgeOptions="badgeOptions"
              :selected="selected"
              :dayHeight="dayHeight"
              :lunar="lunar"
              :themeColor="themeColor"
              :dayColor="dayColor"
              :daySize="daySize"
              :dayBold="dayBold"
              :lunarColor="lunarColor"
              :lunarSize="lunarSize"
              :startText="startText"
              :endText="endText"
              :month="data.nextMonth"
              :foldStatus="data.foldStatus"
              :showMonth="showMonth"
              @change="choiceDate"
            ></mxp-calendar-month>
          </swiper-item>
        </swiper>
        <!-- 今显示当月 解决放到 swiper中 可以左右滑动问题（H5 中） -->
        <view v-else class="swiper">
          <mxp-calendar-month
            :monthViewData="data.currentMonthViewData"
            :selectedDayData="data.selectedDayData"
            :selected="selected"
            :dayHeight="dayHeight"
            :badgeOptions="badgeOptions"
            :lunar="lunar"
            :themeColor="themeColor"
            :dayColor="dayColor"
            :daySize="daySize"
            :dayBold="dayBold"
            :lunarColor="lunarColor"
            :lunarSize="lunarSize"
            :startText="startText"
            :endText="endText"
            :month="data.currentMonth"
            :foldStatus="data.foldStatus"
            :showMonth="showMonth"
            @change="choiceDate"
          ></mxp-calendar-month>
        </view>
      </view>
      <view v-if="fold" class="fold-view" @click="FoldClick">
        <mxp-icon :name="data.foldStatus ? 'shangjiantou' : 'xiajiantou'" size="18px"></mxp-icon>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { type CSSProperties, computed, nextTick, onBeforeMount, reactive, ref, shallowReactive, watch } from 'vue';
  import type { $BadgeProps } from '../mxp-badge/mxp-badge.vue';
  import MxpLine from '../mxp-line/mxp-line.vue';
  import MxpToolbar from '../mxp-toolbar/mxp-toolbar.vue';
  import MxpIcon from '../mxp-icon/mxp-icon.vue';
  import type { $AllColorStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import MxpCalendarMonth from './components/mxp-calendar-month/mxp-calendar-month.vue';
  import Calendar, { type $NewDate } from './util';
  import type { Solar2lunar } from '../mxp-tool/sdk/solarlunar-es';
  import { getXPNumber } from '../mxp-tool/utils/dom';
  import type { PickerViewOnChangeEvent, SwiperOnChangeEvent } from 'mxp-uni-types';

  export type $CalendarViewSelected = {
    [key: string]: unknown;
    /** 日期 */
    date: string;
    /** 顶部信息 */
    topInfo?: string;
    /** 顶部信息颜色 */
    topInfoColor?: $AllColorStr;
    /** 底部信息颜色 */
    topInfoSize?: $SizeUnitStr;
    /** 底部信息 */
    bottomInfo?: string;
    /** 底部信息颜色 */
    bottomInfoColor?: $AllColorStr;
    /** 底部信息颜色 */
    bottomInfoSize?: $SizeUnitStr;
    /** 徽标的内容 */
    badge?: number | string;
    /** 徽标组件的配置 （同mxp-badge组件的props） */
    badgeOptions?: $BadgeProps;
    /** 是否禁用 */
    disable?: boolean;
  };

  export type $CalendarViewResult = {
    range: { before: string; after: string; data: string[] };
    multiple: string[];
    mode?: 'multiple' | 'range' | 'single';
    type?: 'month' | 'week';
    year?: number;
    month: number;
    date: number;
    fulldate: string | undefined;
    lunar: Solar2lunar;
    extraInfo?: $CalendarViewSelected;
    foldStatus?: boolean;
  };

  export interface $CalendarViewProps {
    /** 每一天高度 */
    dayHeight?: $SizeUnitStr;
    /** 自定义当前时间 */
    date?: string[] | string;
    /** 日历类型 */
    type?: 'month' | 'week';
    /** 日期选择模式 */
    mode?: 'multiple' | 'range' | 'single';
    /** 是否使用默认日期  */
    useToday?: boolean;
    /** 徽标的配置  */
    badgeOptions?: $BadgeProps;
    /** 是否使用折叠功能 */
    fold?: boolean;
    /** 背景颜色 */
    background?: $AllColorStr;
    /** 主题色 */
    themeColor?: $AllColorStr;
    /** 日期字体颜色 */
    dayColor?: $AllColorStr;
    /** 日期字体大小 */
    daySize?: $SizeUnitStr;
    /** 日期字体加粗 */
    dayBold?: boolean;
    /** 农历字体颜色 */
    lunarColor?: $AllColorStr;
    /** 农历字体大小 */
    lunarSize?: $SizeUnitStr;
    /** mode=range时，第一个日期底部的提示文字 */
    startText?: string;
    /** mode=range时，最后一个日期底部的提示文字 */
    endText?: string;
    /** 日历以周几开始 */
    startWeek?: 'mon' | 'sun';
    /** 打点，期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] */
    selected?: $CalendarViewSelected[];
    /** 是否显示农历 */
    lunar?: boolean;
    /** 日期选择范围-开始日期 */
    startDate?: string;
    /** 日期选择范围-结束日期 */
    endDate?: string;
    /** 允许日期选择范围内重选结束日期 */
    rangeEndRepick?: boolean;
    /** 允许日期选择范围起始日期为同一天 */
    rangeSameDay?: boolean;
    /** 允许日期选择范围内遇到打点禁用日期进行截断 */
    rangeHaveDisableTruncation?: boolean;
    /** mode==='multiple' 时最多选择几个日期 */
    maxQuantity?: number;
    /** 月份仅显示当月日期 */
    monthShowCurrentMonth?: boolean;
    /** 滑动切换模式，可选值 horizontal:横向; vertical:纵向; none:不使用滑动切换 */
    slideSwitchMode?: 'horizontal' | 'none' | 'vertical';
    /** 是否显示月份为背景 */
    showMonth?: boolean;
    /** 禁止点击日历 */
    disabled?: boolean;
    /** 周几的文字 默认：[ '周一', '周二', '周三', '周四', '周五', '周六','周日'] */
    weekText?: [string, string, string, string, string, string, string];
    /** 周几文字的样式 */
    weekTextStyle?: CSSProperties;
    /** 是否显示线条 */
    showLine?: boolean;
    /** 自定义的样式属性 */
    cusStyle?: CSSProperties;
    /** 自定义的样式属性 */
    cusClass?: string;
  }

  defineOptions({
    name: 'mxp-calendar-view',
  });
  const props = withDefaults(defineProps<$CalendarViewProps>(), {
    dayHeight: '60px',
    date: '',
    type: 'month',
    mode: 'range',
    useToday: undefined,
    badgeOptions: undefined,
    fold: true,
    background: '#ffffff',
    themeColor: '#3c9cff',
    dayColor: '#313131',
    daySize: '18px',
    dayBold: false,
    lunarColor: '#313131',
    lunarSize: '12px',
    startText: '开始',
    endText: '结束',
    startWeek: 'mon',
    selected: () => [],
    lunar: true,
    startDate: '',
    endDate: '',
    rangeEndRepick: false,
    rangeSameDay: false,
    rangeHaveDisableTruncation: false,
    monthShowCurrentMonth: false,
    maxQuantity: Number.MAX_VALUE,
    slideSwitchMode: 'horizontal',
    showMonth: true,
    disabled: false,
    weekTextStyle: () => ({}),
    weekText: () => ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
    cusStyle: () => ({}),
    cusClass: '',
    showLine: true,
  });

  defineSlots<{
    /** 头部 */
    header: () => void;
  }>();

  const emits = defineEmits<{
    (e: 'update:modelValue', data: $CalendarViewResult): void;
    (e: 'change', data: $CalendarViewResult): void;
    (e: 'confirm', data: $CalendarViewResult): void;
    (e: 'monthSwitch', event: { year: number; month: number }): void;
    (e: 'foldSwitch', event: { type: 'month' | 'week' | 'year'; status: boolean }): void;
    (e: 'pickerState', state: boolean): void;
  }>();

  const newDayHeight = computed(() => getXPNumber(props.dayHeight));
  const newUseToday = computed(() => (typeof props.useToday !== 'undefined' ? props.useToday : props.mode === 'single'));

  const data = reactive({
    /** 上月的展示数据 */
    preMonthViewData: {} as Record<number, $NewDate[]>,
    /** 当月的展示数据 */
    currentMonthViewData: {} as Record<number, $NewDate[]>,
    /** 下月的展示数据 */
    nextMonthViewData: {} as Record<number, $NewDate[]>,
    /** 上月的月份 */
    preMonth: '' as number | string,
    /** 当前月份 */
    currentMonth: '' as number | string,
    /** 下月的月份 */
    nextMonth: '' as number | string,
    /** 选中的日期数据 */
    selectedDayData: {} as $NewDate,
    /** 今天的日期数据 */
    todayData: undefined as $NewDate | undefined,
    /** 轮播图当前页 */
    swiperCurrent: (props.slideSwitchMode === 'none' ? 0 : 1) as 0 | 1 | 2,
    /** 轮播图切换方向 */
    swiperChangeDirection: undefined as 'next' | 'pre' | undefined,
    /** 选择的时间 */
    pickerDate: '' as string[] | string,
    /** 日历折叠状态 */
    foldStatus: false,
    /** 记录上次使用周日期 */
    preWeekDate: undefined as string[] | string | undefined,
  });
  const pickerdata = shallowReactive({
    show: false,
    yearList: getYearList(),
    monthList: getMonthList(),
    value: [new Date().getFullYear() - 1900, new Date().getMonth()],
  });
  const calendarInstance = ref<Calendar | null>(null);

  const swiperStyle = computed<CSSProperties>(() => {
    const style: CSSProperties = { padding: '10px 0' };
    style.height = data.foldStatus ? `${newDayHeight.value * 6}px` : `${newDayHeight.value}px`;
    return style;
  });

  const getDateType = computed(() => (data.foldStatus ? 'month' : 'week'));

  watch(
    () => props.date,
    (newVal) => {
      calendarInstance.value!.cleanRange();
      init(newVal);
      data.pickerDate = newVal;
    }
  );
  watch(
    () => props.mode,
    (newVal) => {
      calendarInstance.value!.cleanRange();
      calendarInstance.value!.resetMode(newVal);
      init(props.date);
    }
  );

  watch(
    () => props.startDate,
    (val) => {
      calendarInstance.value!.resetSatrtDate(val);
      calendarInstance.value!.setDate(data.todayData!.fullDate || '');
      assignmentWeeks();
    }
  );

  watch(
    () => props.endDate,
    (val) => {
      calendarInstance.value!.resetEndDate(val);
      calendarInstance.value!.setDate(data.todayData!.fullDate || '');
      assignmentWeeks();
    }
  );

  watch(
    () => props.monthShowCurrentMonth,
    (val) => {
      calendarInstance.value!.resetMonthShowCurrentMonth(val);
      setDate(data.todayData!.fullDate || '');
    }
  );

  watch(
    () => props.rangeEndRepick,
    (val) => {
      calendarInstance.value!.resetRangeEndRepick(val);
    }
  );
  watch(
    () => props.rangeSameDay,
    (val) => {
      calendarInstance.value!.resetRangeSameDay(val);
    }
  );

  watch(
    () => props.rangeHaveDisableTruncation,
    (val) => {
      calendarInstance.value!.resetRangeHaveDisableTruncation(val);
      calendarInstance.value!.cleanRange();
      init(props.date);
    }
  );

  watch(
    () => props.selected,
    (newVal) => {
      calendarInstance.value!.setSelectInfo(data.todayData!.fullDate || '', newVal);
      assignmentWeeks();
    },
    { deep: true }
  );

  watch(
    () => props.type,
    () => {
      initFold();
      calendarInstance.value!.resetFoldStatus(data.foldStatus);
      init(props.date);
    }
  );

  watch(
    () => props.startWeek,
    (newVal) => {
      calendarInstance.value!.cleanRange();
      calendarInstance.value!.resetStartWeek(newVal);
      init(props.date);
    }
  );

  watch(
    () => pickerdata.show,
    (val) => {
      emits('pickerState', val);
    }
  );

  onBeforeMount(() => {
    initFold();
    calendarInstance.value = new Calendar({
      selected: props.selected,
      startDate: props.startDate,
      endDate: props.endDate,
      mode: props.mode,
      type: props.type,
      startWeek: props.startWeek,
      foldStatus: data.foldStatus,
      monthShowCurrentMonth: props.monthShowCurrentMonth,
      rangeEndRepick: props.rangeEndRepick,
      rangeSameDay: props.rangeSameDay,
      rangeHaveDisableTruncation: props.rangeHaveDisableTruncation,
    });
    init(props.date);
  });

  /** 日期选择 */
  function openPickerDate() {
    const now = data.todayData;
    if (now) {
      pickerdata.value = [Number(now.year) - 1900, Number(now.month) - 1];
    } else {
      pickerdata.value = [new Date().getFullYear() - 1900, new Date().getMonth()];
    }
    pickerdata.show = true;
  }

  /**
   * 初始化日期显示
   */
  function init(date: string[] | string) {
    const firstDate = (props.mode === 'single' ? date : date[0]) as string;
    // 如果填写默认值
    if (date) {
      // 根据类型默认选中不同的值
      if (props.mode === 'single') {
        // 记录上一次使用的周日期
        data.preWeekDate = date;
      } else if (props.mode === 'multiple') {
        calendarInstance.value!.multiple = date as string[];
        calendarInstance.value!._getWeek(calendarInstance.value!.multiple[calendarInstance.value!.multiple.length - 1] || '');
        // 记录上一次使用的周日期
        data.preWeekDate = date[0];
      } else if (props.mode === 'range') {
        date[0] && calendarInstance.value!.setRange(date[0]);
        date[1] && calendarInstance.value!.setRange(date[1]);
        // 记录上一次使用的周日期
        data.preWeekDate = date[0];
      }
    }
    // 如果不填写默认值 且 使用今日作为默认值 并且 还没有在打点中禁用今天的日期
    else if (
      newUseToday.value &&
      !props.selected.filter((item) => item.disable && calendarInstance.value!.dateEqual(item.date, calendarInstance.value!.date.fullDate)).length
    ) {
      if (props.mode === 'multiple') {
        calendarInstance.value!.multiple = [calendarInstance.value!.date.fullDate];
        calendarInstance.value!._getWeek(calendarInstance.value!.multiple[calendarInstance.value!.multiple.length - 1] || '');
      } else if (props.mode === 'range') {
        calendarInstance.value!.setRange(calendarInstance.value!.date.fullDate);
      }
      // 记录上一次使用的周日期
      data.preWeekDate = calendarInstance.value!.date.fullDate;
    }

    // 设置日期
    calendarInstance.value!.setDate(firstDate);
    // 本月日期信息
    data.currentMonthViewData = calendarInstance.value!.weeks;
    // 现在的日期
    data.todayData = calendarInstance.value!.getInfo(firstDate);
    // 设置当前月份
    data.currentMonth = data.todayData!.month;
    // 如果不填写默认值 且 使用今日作为默认值
    if ((newUseToday.value && !props.date) || props.date) {
      data.selectedDayData = data.todayData!;
    }
    // 防止一次渲染过多数据，造成对nvue的卡顿
    nextTick(() => {
      // 如果使用滑动模式
      if (props.slideSwitchMode !== 'none') {
        const fullDate = data.todayData!.fullDate || '';
        data.preMonthViewData = calendarInstance.value!._getWeek(calendarInstance.value!.getDate(fullDate, -1, getDateType.value).fullDate, false);
        data.preMonth = calendarInstance.value!.getDate(fullDate, -1, getDateType.value).month;
        data.nextMonthViewData = calendarInstance.value!._getWeek(calendarInstance.value!.getDate(fullDate, +1, getDateType.value).fullDate, false);
        data.nextMonth = calendarInstance.value!.getDate(fullDate, +1, getDateType.value).month;
      }
    });
  }

  /** 滚轮改变 */
  function changePicker(e: PickerViewOnChangeEvent) {
    pickerdata.value = e.detail.value;
  }
  /** 滚轮改变 */
  function cancelPicker() {
    pickerdata.show = false;
  }

  /** 滚轮改变 */
  function submitPicker() {
    pickerdata.show = false;
    const dateStr = `${pickerdata.value[0]! + 1900}-${pickerdata.value[1]! + 1}-01`;
    setDate(dateStr);
    const { year, month } = calendarInstance.value!.getDate(dateStr);
    emits('monthSwitch', { year, month: Number(month) });
  }

  /**
   * 变化触发
   */
  function change() {
    setEmit('change');
    setEmit('update:modelValue');
  }

  /**
   * 选择月份触发
   */
  function monthSwitch() {
    const { year, month } = data.todayData!;
    emits('monthSwitch', { year: year!, month: Number(month) });
  }

  /**
   * 派发事件
   */
  function setEmit(name: 'change' | 'confirm' | 'update:modelValue') {
    const { year, month, date, fullDate, lunar, extraInfo, type, mode } = data.selectedDayData;

    const params: $CalendarViewResult = {
      range: calendarInstance.value!.rangeStatus,
      multiple: calendarInstance.value!.multiple,
      mode,
      type,
      year,
      month: Number(month),
      date,
      fulldate: fullDate,
      lunar,
      extraInfo,
    };

    if (props.type === 'month' || props.type === 'week') {
      params.foldStatus = data.foldStatus;
    }
    if (name === 'change') {
      emits('change', params);
    } else if (name === 'update:modelValue') {
      emits('update:modelValue', params);
    } else if (name === 'confirm') {
      emits('confirm', params);
    }
  }

  /**
   * 选择天触发
   */
  function choiceDate(dayData: $NewDate) {
    // 如果为禁用 或者 空数据 或者 禁止点击日期
    if (dayData.disable || dayData.empty || props.disabled) {
      return;
    }

    data.selectedDayData = dayData;
    // 保存周日历操作日历
    data.preWeekDate = data.selectedDayData.fullDate;
    // 设置选择范围
    calendarInstance.value!.setRange(data.selectedDayData.fullDate || '');
    // 设置多选
    calendarInstance.value!.setMultiple(data.selectedDayData.fullDate || '', props.maxQuantity);
    // 如果启用滑动切换 且当前模式为范围选择时则重新计算上月与下月
    if (props.slideSwitchMode !== 'none') {
      let monthViewName: 'currentMonthViewData' | 'nextMonthViewData' | 'preMonthViewData' | undefined = undefined;
      switch (data.swiperCurrent) {
        case 0:
          monthViewName = 'preMonthViewData';
          if (props.mode === 'range') {
            data.currentMonthViewData = calendarInstance.value!._getWeek(data.currentMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
            data.nextMonthViewData = calendarInstance.value!._getWeek(data.nextMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
          }
          break;
        case 2:
          monthViewName = 'nextMonthViewData';
          if (props.mode === 'range') {
            data.currentMonthViewData = calendarInstance.value!._getWeek(data.currentMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
            data.preMonthViewData = calendarInstance.value!._getWeek(data.preMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
          }
          break;
        default:
          monthViewName = 'currentMonthViewData';
          if (props.mode === 'range') {
            data.preMonthViewData = calendarInstance.value!._getWeek(data.preMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
            data.nextMonthViewData = calendarInstance.value!._getWeek(data.nextMonthViewData[0]!.find((item) => item.fullDate)!.fullDate || '', false);
          }
          break;
      }

      data[monthViewName] = calendarInstance.value!.weeks;
    } else {
      data.currentMonthViewData = calendarInstance.value!.weeks;
    }

    change();
  }

  /**
   * 回到今天
   */
  function backToday() {
    const _date = new Date();
    const dateStr = `${_date.getFullYear()}-${_date.getMonth() + 1}-01`;
    const { year, month } = calendarInstance.value!.getDate(dateStr);
    if (Number(year) !== Number(data.todayData?.year) || Number(month) !== Number(data.todayData?.month)) {
      setDate(dateStr);
      emits('monthSwitch', { year, month: Number(month) });
    }
  }

  /**
   * 上个月
   */
  function onPre() {
    data.swiperChangeDirection = 'pre';
    updateMonthViewData();
  }

  /**
   * 下个月
   */
  function onNext() {
    data.swiperChangeDirection = 'next';
    updateMonthViewData();
  }

  /**
   * 设置日期
   */
  function setDate(date: string, recordPreWeeksDate = false) {
    calendarInstance.value!.setDate(date);
    assignmentWeeks(recordPreWeeksDate);
    data.todayData = calendarInstance.value!.getInfo(date);
  }

  /**
   * 用来将calendarInstance.value.weeks 赋值到 weeks
   */
  function assignmentWeeks(recordPreWeeksDate = false) {
    let monthViewName: 'currentMonthViewData' | 'nextMonthViewData' | 'preMonthViewData' | undefined = undefined;
    let weekMonthName: 'currentMonth' | 'nextMonth' | 'preMonth' | undefined = undefined;
    switch (data.swiperCurrent) {
      case 0:
        monthViewName = 'preMonthViewData';
        weekMonthName = 'preMonth';
        break;
      case 2:
        monthViewName = 'nextMonthViewData';
        weekMonthName = 'nextMonth';
        break;
      default:
        monthViewName = 'currentMonthViewData';
        weekMonthName = 'currentMonth';
        break;
    }
    nextTick(() => {
      data[monthViewName!] = calendarInstance.value!.weeks;
      nextTick(() => {
        data[weekMonthName!] = calendarInstance.value!.selectDate!.month;
        if (recordPreWeeksDate) {
          /**
           * 记录preWeeksDate的优先级，只要满足一项则不在往下走，不满足则继续走
           * . 判断本月有没有当前mode中选中的值
           * . 判断本月有没有打点的日期默认是最后一个
           * . 找出本月第一个带有fulllDate的日期
           */
          if (props.mode === 'single') {
            const newPreWeekDate = calendarInstance.value!.canlender.filter((item) => item.fullDate === data.selectedDayData.fullDate);
            if (newPreWeekDate.length) {
              data.preWeekDate = newPreWeekDate[0]!.fullDate;
              return;
            }
          } else if (props.mode === 'range') {
            const afterDate = calendarInstance.value!.canlender.filter((item) => item.fullDate === calendarInstance.value!.rangeStatus.after);
            if (afterDate.length) {
              data.preWeekDate = afterDate[0]!.fullDate;
              return;
            }

            const beforeDate = calendarInstance.value!.canlender.filter((item) => item.fullDate === calendarInstance.value!.rangeStatus.before);
            if (beforeDate.length) {
              data.preWeekDate = beforeDate[0]!.fullDate;
              return;
            }
          } else if (props.mode === 'multiple' || props.selected.length) {
            // multiple中找出本月的数据
            const newMultiple = calendarInstance.value!.multiple.filter((item) => Number(item.split('-')[1]) === Number(data[weekMonthName!]));
            // 从最后一个往前找
            for (let i = newMultiple.length - 1; i >= 0; i--) {
              const multiple = newMultiple[i];
              const newPreWeekDate = calendarInstance.value!.canlender.filter((item) => item.fullDate === multiple);
              if (newPreWeekDate.length) {
                data.preWeekDate = newPreWeekDate[0]!.fullDate;
                return;
              }
            }
          }

          if (props.selected.length) {
            // selected中找出本月的数据
            const newSelected = props.selected.filter((item) => Number(item.date.split('-')[1]) === Number(data[weekMonthName!]));
            // 从最后一个往前找
            for (let i = newSelected.length - 1; i >= 0; i--) {
              const selected = newSelected[i]!;
              const newPreWeekDate = calendarInstance.value!.canlender.filter((item) => item.fullDate === selected.date);
              if (newPreWeekDate.length) {
                data.preWeekDate = newPreWeekDate[0]!.fullDate;
                return;
              }
            }
          }

          // eslint-disable-next-line @typescript-eslint/prefer-for-of
          for (let i = 0; i < calendarInstance.value!.canlender.length; i++) {
            const dateDate = calendarInstance.value!.canlender[i]!;
            if (dateDate.fullDate) {
              data.preWeekDate = dateDate.fullDate;
              return;
            }
          }
        }
      });
    });
  }

  /**
   * 滑动切换日期
   */
  function swiperChange(e: SwiperOnChangeEvent) {
    // 非用户滑动不执行
    if (e.detail.source === 'touch') {
      const curr = e.detail.current as 0 | 1 | 2;
      if (curr - data.swiperCurrent === 1 || curr - data.swiperCurrent === -2) {
        data.swiperChangeDirection = 'next';
      } else {
        data.swiperChangeDirection = 'pre';
      }
      data.swiperCurrent = curr;
      updateMonthViewData();
    }
  }

  /**
   * 更新
   * @param isChange - 是否使当前的视图发生变化
   */
  function updateMonthViewData(isChange = true) {
    const addDayCount = isChange ? (data.swiperChangeDirection === 'next' ? +1 : -1) : 0;
    setDate(calendarInstance.value!.getDate(data.todayData!.fullDate || '', addDayCount, getDateType.value).fullDate, true);
    swiperCurrentChangeWeeks();
    monthSwitch();
    data.pickerDate = data.todayData!.fullDate!;
  }

  /**
   * swiperCurrent改变需要改动的weeks
   */
  function swiperCurrentChangeWeeks() {
    // 防止一次渲染过多数据，造成对nvue的卡顿
    nextTick(() => {
      if (props.slideSwitchMode !== 'none') {
        if (data.swiperChangeDirection === 'next') {
          const newDate = calendarInstance.value!.getDate(data.todayData!.fullDate || '', +1, getDateType.value);
          const newWeeks = calendarInstance.value!._getWeek(newDate.fullDate, false);
          const newMonth = newDate.month;

          if (data.swiperCurrent === 0) {
            data.currentMonthViewData = newWeeks;
            data.currentMonth = newMonth;
          } else if (data.swiperCurrent === 1) {
            data.nextMonthViewData = newWeeks;
            data.nextMonth = newMonth;
          } else {
            data.preMonthViewData = newWeeks;
            data.preMonth = newMonth;
          }
        } else {
          const newDate = calendarInstance.value!.getDate(data.todayData!.fullDate || '', -1, getDateType.value);
          const newWeeks = calendarInstance.value!._getWeek(newDate.fullDate, false);
          const newMonth = newDate.month;

          if (data.swiperCurrent === 0) {
            data.nextMonthViewData = newWeeks;
            data.nextMonth = newMonth;
          } else if (data.swiperCurrent === 1) {
            data.preMonthViewData = newWeeks;
            data.preMonth = newMonth;
          } else {
            data.currentMonthViewData = newWeeks;
            data.currentMonth = newMonth;
          }
        }
      }
    });
  }

  /** 点击折叠 */
  function FoldClick() {
    data.foldStatus = !data.foldStatus;
    calendarInstance.value!.resetFoldStatus(data.foldStatus);
    // 重置当前weeks
    setDate(Array.isArray(data.preWeekDate) ? data.preWeekDate[0]! : data.preWeekDate!);
    nextTick(() => {
      // 重置左右切换的上下weeks
      if (props.slideSwitchMode !== 'none') {
        const nextDate = calendarInstance.value!.getDate(data.todayData!.fullDate || '', +1, getDateType.value);
        const nextWeeks = calendarInstance.value!._getWeek(nextDate.fullDate, false);
        const nextMonth = nextDate.month;

        const preDate = calendarInstance.value!.getDate(data.todayData!.fullDate || '', -1, getDateType.value);
        const preWeeks = calendarInstance.value!._getWeek(preDate.fullDate, false);
        const preMonth = preDate.month;

        if (data.swiperChangeDirection === 'next') {
          if (data.swiperCurrent === 0) {
            data.currentMonthViewData = nextWeeks;
            data.currentMonth = nextMonth;
            data.nextMonthViewData = preWeeks;
            data.nextMonth = preMonth;
          } else if (data.swiperCurrent === 1) {
            data.nextMonthViewData = nextWeeks;
            data.nextMonth = nextMonth;
            data.preMonthViewData = preWeeks;
            data.preMonth = preMonth;
          } else {
            data.preMonthViewData = nextWeeks;
            data.preMonth = nextMonth;
            data.currentMonthViewData = preWeeks;
            data.currentMonth = preMonth;
          }
        } else {
          if (data.swiperCurrent === 0) {
            data.nextMonthViewData = preWeeks;
            data.nextMonth = preMonth;
            data.currentMonthViewData = nextWeeks;
            data.currentMonth = nextMonth;
          } else if (data.swiperCurrent === 1) {
            data.preMonthViewData = preWeeks;
            data.preMonth = preMonth;
            data.nextMonthViewData = nextWeeks;
            data.nextMonth = nextMonth;
          } else {
            data.currentMonthViewData = preWeeks;
            data.currentMonth = preMonth;
            data.preMonthViewData = nextWeeks;
            data.preMonth = nextMonth;
          }
        }
      }
    });
    // fold切换事件
    emits('foldSwitch', { type: props.type, status: data.foldStatus });
  }

  /** 初始化折叠 */
  function initFold() {
    if (props.type === 'month' || props.type === 'week') {
      data.foldStatus = props.type === 'month';
    }
  }

  /** 得到年份列表 */
  function getYearList() {
    const list = [];
    for (let i = 1900; i <= 2100; i++) {
      list.push(i);
    }
    return list;
  }

  /** 得到月份列表 */
  function getMonthList() {
    const list = [];
    for (let i = 1; i <= 12; i++) {
      list.push(i < 10 ? `0${i}` : `${i}`);
    }
    return list;
  }
  defineExpose({
    /** 切换折叠状态 */
    switchFold: FoldClick,
    /** 切换前一个月 */
    switchFrontMonth: onPre,
    /** 切换后一个月 */
    switchNextMonth: onNext,
    /** 得到今天的日期数据 */
    getTodayData: () => data.todayData,
  });
</script>

<style lang="scss" scoped>
  .mxp-calendar {
    display: flex;
    flex-direction: column;
  }

  .header-view {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    height: 48px;

    .header-title {
      font-size: 16px;
      color: #333333;
      text-align: center;
    }

    .header-btn-box {
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: center;
      width: 48px;
      height: 48px;
    }

    .back-today {
      position: absolute;
      top: 10px;
      right: 0;
      height: 28px;
      padding: 0 6px;
      padding-left: 14px;
      font-size: 12px;
      line-height: 28px;
      color: #ffffff;
      border-top-left-radius: 24px;
      border-bottom-left-radius: 24px;
    }
  }

  .mxp-calendar-vessel {
    position: relative;

    .swiper {
      transition: height 0.3s linear;
    }

    .weeks-view {
      position: relative;
      display: flex;
      flex-direction: row;
      padding: 0 4px;

      .week-day-view {
        display: flex;
        flex: 1;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 42px;
      }

      .day-text {
        font-size: 14px;
      }
    }
  }

  .fold-view {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    height: 40px;
    margin-top: -10px;
  }

  .picker-view {
    height: 200px;

    .picker-item {
      display: flex;
      flex-flow: column nowrap;
      align-items: center;
      justify-content: center;
      height: 40px;
      font-size: 13px;
      line-height: 40px;
      text-align: center;
    }
  }
</style>
